using System.Collections.Generic;
using Xunit;

namespace CodePlex.TfsLibrary
{
    public class DiffEngineTest
    {
        static void AssertCorrectChunk(DiffEngine.Chunk chunk,
                                       DiffEngine.ChunkType type,
                                       params string[] lines)
        {
            Assert.Equal(type, chunk.ChunkType);
            Assert.Equal(lines.Length, chunk.LineCount);

            for (int idx = 0; idx < lines.Length; idx++)
                Assert.Equal(lines[idx], chunk.Lines[idx]);
        }

        [Fact]
        public void DifferenceAtBottomOfLeft()
        {
            //    a     a
            //    b     b
            //  - c

            string[] left = new string[] { "a", "b", "c" };
            string[] right = new string[] { "a", "b" };

            List<DiffEngine.Chunk> result = DiffEngine.GetDiff(left, right);

            Assert.Equal(2, result.Count);
            Assert.Equal(DiffEngine.ChunkType.Same, result[0].ChunkType);
            Assert.Equal(DiffEngine.ChunkType.Left, result[1].ChunkType);
        }

        [Fact]
        public void DifferenceAtBottomOfRight()
        {
            //    a     a
            //    b     b
            //  +       c

            string[] left = new string[] { "a", "b" };
            string[] right = new string[] { "a", "b", "c" };

            List<DiffEngine.Chunk> result = DiffEngine.GetDiff(left, right);

            Assert.Equal(2, result.Count);
            Assert.Equal(DiffEngine.ChunkType.Same, result[0].ChunkType);
            Assert.Equal(DiffEngine.ChunkType.Right, result[1].ChunkType);
        }

        [Fact]
        public void DifferenceAtTopOfLeft()
        {
            //  - @
            //    a     a
            //    b     b
            //    c     c

            string[] left = new string[] { "@", "a", "b", "c" };
            string[] right = new string[] { "a", "b", "c" };

            List<DiffEngine.Chunk> result = DiffEngine.GetDiff(left, right);

            Assert.Equal(2, result.Count);
            Assert.Equal(DiffEngine.ChunkType.Left, result[0].ChunkType);
            Assert.Equal(DiffEngine.ChunkType.Same, result[1].ChunkType);
        }

        [Fact]
        public void DifferenceAtTopOfLeftAndBottomOfRight()
        {
            //  - @
            //    a     a
            //    b     b
            //  +       c

            string[] left = new string[] { "@", "a", "b" };
            string[] right = new string[] { "a", "b", "c" };

            List<DiffEngine.Chunk> result = DiffEngine.GetDiff(left, right);

            Assert.Equal(3, result.Count);
            Assert.Equal(DiffEngine.ChunkType.Left, result[0].ChunkType);
            Assert.Equal(DiffEngine.ChunkType.Same, result[1].ChunkType);
            Assert.Equal(DiffEngine.ChunkType.Right, result[2].ChunkType);
        }

        [Fact]
        public void DifferenceAtTopOfRight()
        {
            //  +       @
            //    a     a
            //    b     b
            //    c     c

            string[] left = new string[] { "a", "b", "c" };
            string[] right = new string[] { "@", "a", "b", "c" };

            List<DiffEngine.Chunk> result = DiffEngine.GetDiff(left, right);

            Assert.Equal(2, result.Count);
            Assert.Equal(DiffEngine.ChunkType.Right, result[0].ChunkType);
            Assert.Equal(DiffEngine.ChunkType.Same, result[1].ChunkType);
        }

        [Fact]
        public void DifferenceAtTopOfRightAndBottomOfLeft()
        {
            //  +       @
            //    a     a
            //    b     b
            //  - c

            string[] left = new string[] { "a", "b", "c" };
            string[] right = new string[] { "@", "a", "b" };

            List<DiffEngine.Chunk> result = DiffEngine.GetDiff(left, right);

            Assert.Equal(3, result.Count);
            Assert.Equal(DiffEngine.ChunkType.Right, result[0].ChunkType);
            Assert.Equal(DiffEngine.ChunkType.Same, result[1].ChunkType);
            Assert.Equal(DiffEngine.ChunkType.Left, result[2].ChunkType);
        }

        [Fact]
        public void DifferenceInMiddleOfLeft()
        {
            //    a     a
            //  - b
            //    c     c

            string[] left = new string[] { "a", "b", "c" };
            string[] right = new string[] { "a", "c" };

            List<DiffEngine.Chunk> result = DiffEngine.GetDiff(left, right);

            Assert.Equal(3, result.Count);
            Assert.Equal(DiffEngine.ChunkType.Same, result[0].ChunkType);
            Assert.Equal(DiffEngine.ChunkType.Left, result[1].ChunkType);
            Assert.Equal(DiffEngine.ChunkType.Same, result[2].ChunkType);
        }

        [Fact]
        public void DifferenceInMiddleOfRight()
        {
            //    a     a
            //  +       b
            //    c     c

            string[] left = new string[] { "a", "c" };
            string[] right = new string[] { "a", "b", "c" };

            List<DiffEngine.Chunk> result = DiffEngine.GetDiff(left, right);

            Assert.Equal(3, result.Count);
            Assert.Equal(DiffEngine.ChunkType.Same, result[0].ChunkType);
            Assert.Equal(DiffEngine.ChunkType.Right, result[1].ChunkType);
            Assert.Equal(DiffEngine.ChunkType.Same, result[2].ChunkType);
        }

        [Fact]
        public void DiffIsCaseSensitive()
        {
            string[] left = new string[] { "a" };
            string[] right = new string[] { "A" };

            List<DiffEngine.Chunk> result = DiffEngine.GetDiff(left, right);

            Assert.Equal(2, result.Count);
            AssertCorrectChunk(result[0], DiffEngine.ChunkType.Left, "a");
            AssertCorrectChunk(result[1], DiffEngine.ChunkType.Right, "A");
        }

        [Fact]
        public void DiffIsWhitespaceSensitive()
        {
            string[] left = new string[] { "a" };
            string[] right = new string[] { "a " };

            List<DiffEngine.Chunk> result = DiffEngine.GetDiff(left, right);

            Assert.Equal(2, result.Count);
            AssertCorrectChunk(result[0], DiffEngine.ChunkType.Left, "a");
            AssertCorrectChunk(result[1], DiffEngine.ChunkType.Right, "a ");
        }

        [Fact]
        public void EmptyFilesYieldEmptyDiffList()
        {
            string[] left = new string[0];
            string[] right = new string[0];

            List<DiffEngine.Chunk> result = DiffEngine.GetDiff(left, right);

            Assert.Equal(0, result.Count);
        }

        [Fact]
        public void InterleavedChangesGoLeftFirst()
        {
            //    a     a
            //  - b
            //  +       c
            //    d     d

            string[] left = new string[] { "a", "b", "d" };
            string[] right = new string[] { "a", "c", "d" };

            List<DiffEngine.Chunk> result = DiffEngine.GetDiff(left, right);

            Assert.Equal(4, result.Count);
            Assert.Equal(DiffEngine.ChunkType.Same, result[0].ChunkType);
            Assert.Equal(DiffEngine.ChunkType.Left, result[1].ChunkType);
            Assert.Equal(DiffEngine.ChunkType.Right, result[2].ChunkType);
            Assert.Equal(DiffEngine.ChunkType.Same, result[3].ChunkType);
        }

        [Fact]
        public void MacStyleLineEndingsSupported()
        {
            string left = "a\rb\rc";
            string right = "a\rd\rc";

            List<DiffEngine.Chunk> result = DiffEngine.GetDiff(left, right);

            Assert.Equal(4, result.Count);
            AssertCorrectChunk(result[0], DiffEngine.ChunkType.Same, "a");
            AssertCorrectChunk(result[1], DiffEngine.ChunkType.Left, "b");
            AssertCorrectChunk(result[2], DiffEngine.ChunkType.Right, "d");
            AssertCorrectChunk(result[3], DiffEngine.ChunkType.Same, "c");
        }

        [Fact]
        public void MultiLineLeftEmptyRightYieldsSingleLeftChunk()
        {
            string[] left = new string[] { "a", "b" };
            string[] right = new string[0];

            List<DiffEngine.Chunk> result = DiffEngine.GetDiff(left, right);

            Assert.Equal(1, result.Count);
            AssertCorrectChunk(result[0], DiffEngine.ChunkType.Left, "a", "b");
        }

        [Fact]
        public void MultiLineRightEmptyLeftYieldsSingleRightChunk()
        {
            string[] left = new string[0];
            string[] right = new string[] { "a", "b" };

            List<DiffEngine.Chunk> result = DiffEngine.GetDiff(left, right);

            Assert.Equal(1, result.Count);
            AssertCorrectChunk(result[0], DiffEngine.ChunkType.Right, "a", "b");
        }

        [Fact]
        public void MultipleDifferences()
        {
            //  - a
            //    b     b
            //    c     c
            //  +       d
            //    e     e
            //  +       f
            //  +       g
            //    h     h
            //  - i

            string[] left = new string[] { "a", "b", "c", "e", "h", "i" };
            string[] right = new string[] { "b", "c", "d", "e", "f", "g", "h" };

            List<DiffEngine.Chunk> result = DiffEngine.GetDiff(left, right);

            Assert.Equal(7, result.Count);
            Assert.Equal(DiffEngine.ChunkType.Left, result[0].ChunkType);
            Assert.Equal(DiffEngine.ChunkType.Same, result[1].ChunkType);
            Assert.Equal(DiffEngine.ChunkType.Right, result[2].ChunkType);
            Assert.Equal(DiffEngine.ChunkType.Same, result[3].ChunkType);
            Assert.Equal(DiffEngine.ChunkType.Right, result[4].ChunkType);
            Assert.Equal(DiffEngine.ChunkType.Same, result[5].ChunkType);
            Assert.Equal(DiffEngine.ChunkType.Left, result[6].ChunkType);
        }

        [Fact]
        public void NoBacktrackingAllowed()
        {
            //    a     a
            //  +       c
            //    b     b
            //  - c

            string[] left = new string[] { "a", "b", "c" };
            string[] right = new string[] { "a", "c", "b" };

            List<DiffEngine.Chunk> result = DiffEngine.GetDiff(left, right);

            Assert.Equal(4, result.Count);
            Assert.Equal(DiffEngine.ChunkType.Same, result[0].ChunkType);
            Assert.Equal(DiffEngine.ChunkType.Right, result[1].ChunkType);
            Assert.Equal(DiffEngine.ChunkType.Same, result[2].ChunkType);
            Assert.Equal(DiffEngine.ChunkType.Left, result[3].ChunkType);
        }

        [Fact]
        public void SameMultiLineContentYieldsSingleSameChunk()
        {
            string[] left = new string[] { "a", "b" };
            string[] right = new string[] { "a", "b" };

            List<DiffEngine.Chunk> result = DiffEngine.GetDiff(left, right);

            Assert.Equal(1, result.Count);
            AssertCorrectChunk(result[0], DiffEngine.ChunkType.Same, "a", "b");
        }

        [Fact]
        public void SameSingleLineContentYieldsSingleSameChunk()
        {
            string[] left = new string[] { "a" };
            string[] right = new string[] { "a" };

            List<DiffEngine.Chunk> result = DiffEngine.GetDiff(left, right);

            Assert.Equal(1, result.Count);
            AssertCorrectChunk(result[0], DiffEngine.ChunkType.Same, "a");
        }

        [Fact]
        public void SingleLineLeftEmptyRightYieldsSingleLeftChunk()
        {
            string[] left = new string[] { "a" };
            string[] right = new string[0];

            List<DiffEngine.Chunk> result = DiffEngine.GetDiff(left, right);

            Assert.Equal(1, result.Count);
            AssertCorrectChunk(result[0], DiffEngine.ChunkType.Left, "a");
        }

        [Fact]
        public void SingleLineRightEmptyLeftYieldsSingleRightChunk()
        {
            string[] left = new string[0];
            string[] right = new string[] { "a" };

            List<DiffEngine.Chunk> result = DiffEngine.GetDiff(left, right);

            Assert.Equal(1, result.Count);
            AssertCorrectChunk(result[0], DiffEngine.ChunkType.Right, "a");
        }

        [Fact]
        public void UnixStyleLineEndingsSupported()
        {
            string left = "a\nb\nc";
            string right = "a\nd\nc";

            List<DiffEngine.Chunk> result = DiffEngine.GetDiff(left, right);

            Assert.Equal(4, result.Count);
            AssertCorrectChunk(result[0], DiffEngine.ChunkType.Same, "a");
            AssertCorrectChunk(result[1], DiffEngine.ChunkType.Left, "b");
            AssertCorrectChunk(result[2], DiffEngine.ChunkType.Right, "d");
            AssertCorrectChunk(result[3], DiffEngine.ChunkType.Same, "c");
        }

        [Fact]
        public void WindowsStyleLineEndingsSupported()
        {
            string left = "a\r\nb\r\nc";
            string right = "a\r\nd\r\nc";

            List<DiffEngine.Chunk> result = DiffEngine.GetDiff(left, right);

            Assert.Equal(4, result.Count);
            AssertCorrectChunk(result[0], DiffEngine.ChunkType.Same, "a");
            AssertCorrectChunk(result[1], DiffEngine.ChunkType.Left, "b");
            AssertCorrectChunk(result[2], DiffEngine.ChunkType.Right, "d");
            AssertCorrectChunk(result[3], DiffEngine.ChunkType.Same, "c");
        }
    }
}